﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using AssetLoad;

public class GameObjectPoolManager : MonoBehaviour
{
    private Transform tranCache;
    /// <summary>
    /// 基于原生物体缓冲池
    /// </summary>
    private Dictionary<int, GameObjectPool> pools = new Dictionary<int, GameObjectPool>();
    /// <summary>
    /// 基于resourceManager的缓冲池
    /// </summary>
    private Dictionary<string, GameObjectCounter> resourcePools = new Dictionary<string, GameObjectCounter>();

    /// <summary>
    /// 基于AssetBundleManager的缓冲池
    /// </summary>
    private Dictionary<string, GameObjectCounter> bundlePools = new Dictionary<string, GameObjectCounter>();

    private Dictionary<int, int> idMap = new Dictionary<int, int>();

    public void AddId(int objId, int poolId)
    {
        idMap[objId] = poolId;
    }
    public void RemoveId(int objId)
    {
        if (idMap.ContainsKey(objId))
        {
            idMap.Remove(objId);
        }
    }

    #region 基于assetBundle的方法
    /// <summary>
    /// 注册基于assetBundle方法
    /// </summary>
    /// <param name="bundleName">AssetBundle名称</param>
    /// <param name="assetName">资源名称</param>
    /// <param name="preNum">预支数量</param>
    public void Regist(string bundleName, string assetName, int preNum = 1)
    {
        string key = bundleName + "$" + assetName;
        if (bundlePools.ContainsKey(key))
        {
            bundlePools[key].count++;
            //New(bundlePools[key].prefab);
        }
        else
        {
            Object obj = AssetBundleManager.Instance.GetAssetFromBundle(bundleName, assetName, typeof(GameObject));
            //GameObject pref = Instantiate(obj) as GameObject;
            //pref.name = obj.name;
            //pref.transform.parent = tranCache;
            //New(bundlePools[key].prefab,preNum);
            bundlePools.Add(key, new GameObjectCounter(obj, 1, New(obj, preNum, key)));
        }
    }
    /// <summary>
    /// 基于AssetManager,注册一个对象池
    /// </summary>
    /// <param name="bundleName">bundle名称</param>
    /// <param name="assetName">资源名称</param>
    /// <returns>生产物体</returns>
    public GameObject Spawn(string bundleName, string assetName)
    {
        string key = bundleName + "$" + assetName;
        if (bundlePools.ContainsKey(key))
        {
            return Spawn(bundlePools[key].poolId);
        }
        else
        {
            Debug.LogError("Pool error! It has not exist the asset bundle pool bundleName: " + bundleName + " assetName: " + assetName);
            return null;
        }
    }
    /// <summary>
    /// 注销assetBundle方式的缓冲池
    /// </summary>
    /// <param name="bundleName">assetBundle名称</param>
    /// <param name="assetName">资源名称</param>
    public void UnRegist(string bundleName, string assetName)
    {
        string key = bundleName + "$" + assetName;
        if (bundlePools.ContainsKey(key))
        {
            if (--bundlePools[key].count <= 0)
            {
                RemoveId(bundlePools[key].poolId);
                DestoryPool(bundlePools[key].poolId, key);
                bundlePools.Remove(key);
            }
        }
        else
        {
            Debug.LogError("Unregist pool error! It has not exist the asset pool " + bundleName + " " + assetName);
        }
    }
    #endregion  基于assetBundle的方法

    //void Update() {
    //    int a = 0;
    //}
    #region 基于resourceManager的方法
    /// <summary>
    ///  基于resourceManager,注册一个对象池
    /// </summary>
    /// <param name="resourcePath">资源路径</param>
    public void Regist(string resourcePath, int preNum = 1)
    {
        if (resourcePools.ContainsKey(resourcePath))
        {
            resourcePools[resourcePath].count++;
            //New(resourcePools[resourcePath].prefab);
        }
        else
        {
            Object obj = ResourceManager.Instance.GetResource(resourcePath);
            if (obj == null)
            {
                Debug.LogError("resource " + resourcePath + " hasn't loaded before!!!");
                return;
            }
            //GameObject pref = Instantiate(obj) as GameObject;
            //pref.transform.parent = tranCache;
            resourcePools.Add(resourcePath, new GameObjectCounter(obj, 1, New(obj, preNum)));
        }
    }
    /// <summary>
    /// 使用资源路径生产一个物体
    /// </summary>
    /// <param name="resourcePath">资源路径</param>
    /// <returns>生产的物体</returns>
    public GameObject Spawn(string resourcePath)
    {
        if (resourcePools.ContainsKey(resourcePath))
        {
            return Spawn(resourcePools[resourcePath].poolId);
        }
        else
        {
            Debug.LogError("Pool error! It has not exist the resource pool " + resourcePath);
            return null;
        }
    }
    /// <summary>
    /// 注销resource方式的缓冲池
    /// </summary>
    /// <param name="resourcePath">资源路径</param>
    public void UnRegist(string resourcePath)
    {
        if (resourcePools.ContainsKey(resourcePath))
        {
            if (--resourcePools[resourcePath].count == 0)
            {
                RemoveId(resourcePools[resourcePath].poolId);
                DestoryPool(resourcePools[resourcePath].poolId);
                resourcePools.Remove(resourcePath);
            }
        }
        else
        {
            Debug.LogError("Unregist pool error! It has not exist the resource pool " + resourcePath);
        }
    }


    #endregion 基于resourceManager的方法



    #region 基于gameObject的原始方法
    /// <summary>
    /// 建立一个对象池
    /// </summary>
    /// <param name="obj">对象模板</param>
    public int New(Object obj)
    {
        return New(obj, 1);
    }

    /// <summary>
    /// 建立一个对象池
    /// </summary>
    /// <param name="obj">对象模板</param>
    /// <param name="num">预建立的对象数</param>
    public int New(Object obj, int num)
    {
        int uid = obj.GetInstanceID();
        bool objExist = pools.ContainsKey(uid);

        if (objExist)
        {
            pools[uid].PrePopulate(num);
            pools[uid].HideInHierarchy(tranCache);
        }
        else
        {
            GameObjectPool newPool = new GameObjectPool(obj, num, uid,this);
            pools[newPool.id] = newPool;
            pools[newPool.id].HideInHierarchy(tranCache);
            AddId(uid, uid);
        }
        return uid;
    }

    public int New(Object obj, int num, string key)
    {
        int uid = obj.GetInstanceID();
        bool objExist = pools.ContainsKey(uid);

        if (objExist)
        {
            pools[uid].PrePopulate(num);
            pools[uid].HideInHierarchy(tranCache);
            pools[uid].AddKey(key);
        }
        else
        {
            GameObjectPool newPool = new GameObjectPool(obj, num, uid,this);
            pools[newPool.id] = newPool;
            pools[newPool.id].HideInHierarchy(tranCache);
            pools[newPool.id].AddKey(key);
            AddId(uid, uid);
        }
        return uid;
    }

    /// <summary>
    /// 生产一个物体
    /// </summary>
    /// <param name="obj">模板</param>
    /// <returns>生产的物体</returns>
    public GameObject Spawn(GameObject obj)
    {
        int id;
        if (TryGetPoolId(obj, out id))
        {
            return pools[id].Spawn();
        }
        else
        {
            Debug.LogError("Object " + obj + " doesnt exsit in ObjectPoolManager List.");
            return (GameObject)Instantiate(obj);
        }
    }

    private GameObject Spawn(int poolId)
    {
        if (pools.ContainsKey(poolId))
        {
            return pools[poolId].Spawn();
        }
        else
        {
            Debug.LogError("poolId " + poolId + " doesnt exsit in ObjectPoolManager List.");
            return null;
        }
    }
    /// <summary>
    /// 回收一个物体
    /// </summary>
    /// <param name="obj">要回收的物体</param>
    public void Unspawn(GameObject obj)
    {

        int ID;
        if (TryGetPoolId(obj, out ID))
        {
            obj.transform.parent = tranCache;
            pools[ID].Unspawn(obj);
        }
        else
        {
            Debug.LogError("Object " + obj + " doesnt exsit in ObjectPoolManager List");
            Destroy(obj);
            if (obj != null)
            {
                RemoveId(obj.GetInstanceID());
            }
        }

    }
    /// <summary>
    /// 延迟回收物体
    /// </summary>
    /// <param name="obj">要回收的物体</param>
    /// <param name="duration">延迟时间</param>
    public void Unspawn(GameObject obj, float duration)
    {
        StartCoroutine(UnspawnTimed(obj, duration));
    }

    private IEnumerator UnspawnTimed(GameObject obj, float duration)
    {
        yield return new WaitForSeconds(duration);
        if (obj != null)
        {
            Unspawn(obj);
        }
    }
    /// <summary>
    /// 清空所有物体
    /// </summary>
    public void ClearAll()
    {
        foreach (GameObjectPool pool in pools.Values)
        {
            pool.UnspawnAll();
        }
        pools.Clear();
        bundlePools.Clear();
        resourcePools.Clear();
        idMap.Clear();
    }
    /// <summary>
    /// 清空一个对象池
    /// </summary>
    /// <param name="gObj"></param>
    public void DestoryObjOfPool(GameObject gObj)
    {
        int id;
        if (TryGetPoolId(gObj, out id))
        {
            pools[id].UnspawnAll();
            pools.Remove(id);
            idMap.Remove(id);
        }
    }

    private void DestoryPool(int poolId)
    {
        if (pools.ContainsKey(poolId))
        {
            pools[poolId].UnspawnAll();
            pools.Remove(poolId);
            idMap.Remove(poolId);
        }
        else
        {
            Debug.LogError("pool destroy error of poolId " + poolId);
        }
    }

    private void DestoryPool(int poolId, string key)
    {
        if (pools.ContainsKey(poolId))
        {
            if (pools[poolId].UnspawnAll(key))
            {
                pools.Remove(poolId);
            }
        }
        else
        {
            Debug.LogError("pool destroy error of poolId " + poolId);
        }
    }
    /// <summary>
    /// 获取一个对象的对象池id
    /// </summary>
    /// <param name="obj">物体</param>
    /// <param name="id">返回对象池id</param>
    /// <returns>物体池是否存在</returns>
    public bool TryGetPoolId(GameObject obj, out int id)
    {
        if (obj == null)
        {
            id = 0;
            return false;
        }
        int instId = obj.GetInstanceID();
        if (idMap.ContainsKey(instId))
        {
            id = idMap[instId];
        }
        else
        {
            //idMap[instId] = instId;
            //id = instId;
            id = 0;
        }
        //GameObjectPoolId oid = obj.GetComponent<GameObjectPoolId>();
        //if (oid != null)
        //{
        //    id = oid.id;
        //}
        //else
        //{
        //    id = obj.GetInstanceID();

        //}
        return pools.ContainsKey(id);
    }
    #endregion 基于gameObject的原始方法

    #region 单例
    private void Awake()
    {
        instance = gameObject.GetComponent<GameObjectPoolManager>();
        var obj = new GameObject("cache");
        obj.transform.SetParent(transform);
        obj.SetActive(false);
        tranCache = obj.transform;
    }

    private static GameObjectPoolManager instance;
    public static GameObjectPoolManager Instance
    {
        get
        {
            return instance;
        }
    }
    #endregion 单例
}

public class GameObjectCounter
{
    public Object prefab;
    public int count;
    public int poolId;
    public GameObjectCounter(Object prefab, int count, int poolId)
    {
        this.prefab = prefab;
        this.count = count;
        this.poolId = poolId;
    }
}